#include "Accesser.h"

extern "C" {
#include "tolua++.h"
}
#include "CCLuaEngine.h"
#include "QFile"

static int lua_execute(lua_State *L, int numArgs)
{
    int functionIndex = -(numArgs + 1);
    if (!lua_isfunction(L, functionIndex))
    {
        CCLOG("value at stack [%d] is not function", functionIndex);
        lua_pop(L, numArgs + 1); // remove function and arguments
        return 0;
    }

    int traceback = 0;
    lua_getglobal(L, "__G__TRACKBACK__");                         /* L: ... func arg1 arg2 ... G */
    if (!lua_isfunction(L, -1))
    {
        lua_pop(L, 1);                                            /* L: ... func arg1 arg2 ... */
    }
    else
    {
        lua_insert(L, functionIndex - 1);                         /* L: ... G func arg1 arg2 ... */
        traceback = functionIndex - 1;
    }

    int error = 0;
    error = lua_pcall(L, numArgs, 1, traceback);                  /* L: ... [G] ret */
    if (error)
    {
        if (traceback == 0)
        {
            CCLOG("[LUA ERROR] %s", lua_tostring(L, -1));        /* L: ... error */
            lua_pop(L, 1); // remove error message from stack
        }
        else                                                      /* L: ... [G] error */
        {
            lua_pop(L, 2); // remove __G__TRACKBACK__ and error message from stack
        }
        return 0;
    }

    return 1;
}

static int run_global_func(lua_State* L, const char* functionName, int numArgs)
{
    lua_getglobal(L, functionName);       /* query function by name, stack: function */
    if (!lua_isfunction(L, -1))
    {
        CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);
        lua_pop(L, 1);
        return 0;
    }

    if (numArgs > 0)
    {
        lua_insert(L, -(numArgs + 1));                        /* L: ... func arg1 arg2 ... */
    }
    return lua_execute(L, numArgs);
}

QString Accesser::get(Widget* widget, const QString& name)
{
    CCLuaEngine* e = CCLuaEngine::defaultEngine();
    CCLuaStack* s = e->getLuaStack();
    lua_State* L = s->getLuaState();

    s->pushCCObject(widget, "Widget");
    s->pushString(name.toUtf8());

    run_global_func(L, "get", 2);

    const char* str = lua_tostring(L, -1);
    
    lua_pop(L, lua_gettop(L));

    return str;
}

void Accesser::set(Widget* widget, const QString& name, const QString& value)
{
    CCLuaEngine* e = CCLuaEngine::defaultEngine();
    CCLuaStack* s = e->getLuaStack();
    lua_State* L = s->getLuaState();

    s->pushCCObject(widget, "Widget");
    s->pushString(name.toUtf8());
    s->pushString(value.toUtf8());

    run_global_func(L, "set", 3);

    lua_pop(L, lua_gettop(L));
}

static void push_item_list(lua_State* L, const QList<TreeItem*>& lst)
{
    lua_newtable(L);                                              /* L: table */
    int index = 1;
    for (QList<TreeItem*>::const_iterator it = lst.begin(); it != lst.end(); ++it)
    {
        tolua_pushusertype(L, (void*)*it, "TreeItem");
        lua_rawseti(L, -2, index);          /* table[index] = value, L: table */
        ++index;
    }
}

static void get_item_list(lua_State* L, QList<TreeItem*>& items)
{
    int len = lua_objlen(L, -1);
    for (int i = 1; i <= len; i++) {
        lua_pushinteger(L, i);
        lua_gettable(L, -2);

        TreeItem* item = (TreeItem*)tolua_tousertype(L, -1, 0);
        items.append(item);

        lua_pop(L, 1);
    }
}

static QString run_global_func_with_item_list_return_string(const QList<TreeItem*>& lst, const char* func)
{
    CCLuaEngine* e = CCLuaEngine::defaultEngine();
    CCLuaStack* s = e->getLuaStack();
    lua_State* L = s->getLuaState();

    push_item_list(L, lst);
    run_global_func(L, func, 1);

    const char* str = lua_tostring(L, -1);
    lua_pop(L, lua_gettop(L));

    return str;
}

static QList<TreeItem*> run_global_func_with_string_return_item_list(const QString& str, const char* func)
{
    CCLuaEngine* e = CCLuaEngine::defaultEngine();
    CCLuaStack* s = e->getLuaStack();
    lua_State* L = s->getLuaState();

    s->pushString(str.toUtf8());

    run_global_func(L, func, 1);

    QList<TreeItem*> items;
    get_item_list(L, items);
    lua_pop(L, lua_gettop(L));

    return items;
}
QString Accesser::getCodeCpp(const QList<TreeItem*>& lst)
{
    return run_global_func_with_item_list_return_string(lst, "getCodeCpp");
}

QString Accesser::getCode(const QList<TreeItem*>& lst)
{
    return run_global_func_with_item_list_return_string(lst, "getCode");
}

QString Accesser::toString(const QList<TreeItem*>& lst)
{
    return run_global_func_with_item_list_return_string(lst, "itemTableToString");
}

QList<TreeItem*> Accesser::fromString(const QString& str)
{
    return run_global_func_with_string_return_item_list(str, "itemTableFromString");
}

QList<TreeItem*> Accesser::fromFile(const QString& filePath)
{
    return run_global_func_with_string_return_item_list(filePath, "onOpen");
}
